#pragma once
#include <semaphore.h>
#include<vector>
#include<pthread.h>
#include<cassert>
#include<iostream>
static const int gcap = 5;
template<class T>
class RingQueue
{
private:
    void P(sem_t&sem)
    {
        int n = sem_wait(&sem);
        assert(n==0);
        (void)n;
    }

    void V(sem_t &sem)
    {
        int n = sem_post(&sem);
        assert(n==0);
        (void)n;
    }
public:
    RingQueue(const int& cap =gcap)
    :_cap(gcap)
    ,_queue(cap) // vector的一种构造方式，给一个数字会让vector申请这么大的空间
    {
        int n =sem_init(&_productorSem,0,_cap);
        assert(n == 0);
        sem_init(&_consumerSem,0,0); 
        assert(n == 0);
        pthread_mutex_init(&_pmutex,nullptr);
        pthread_mutex_init(&_cmutex,nullptr);

        _productorStep=_consumerStep=0; //记录生产消费两者的位置
    }

    void push(const T& t)
    {
        // 有一个误区，到底是先申请信号量还是先申请互斥锁
         // 以先我们先申请锁就是因为 i++这种操作不是原子的
         // 现在我们对信号量的加加操作都是原子的，不存在写入不一致问题
        P(_productorSem);
        pthread_mutex_lock(&_pmutex);
        // 在队列中写入数据
        _queue[_productorStep++]=t;
        _productorStep%=_cap;
        pthread_mutex_unlock(&_pmutex);
        V(_consumerSem);
    }

    void pop(T* t)
    {
        P(_consumerSem);
        pthread_mutex_lock(&_cmutex);
        *t= _queue[_consumerStep++];
         _consumerStep%=_cap;
        pthread_mutex_unlock(&_cmutex);
        V(_productorSem);
    }
    ~RingQueue()
    {
        sem_destroy(&_consumerSem);
        sem_destroy(&_productorSem);

        pthread_mutex_destroy(&_pmutex);
        pthread_mutex_destroy(&_cmutex);
    }
private: 
    std::vector<T> _queue; // 队列
    int _cap; // 容量
    sem_t _consumerSem; // 消费者信号量
    sem_t _productorSem; // 生产者信号量

    int _productorStep;
    int _consumerStep;

// 这儿为什么需要两把锁
    pthread_mutex_t _pmutex;
    pthread_mutex_t _cmutex;
};